<?php


namespace app\service\alone\Socket;


use app\service\alone\BaseService;
use think\facade\Log;

class SocketService extends BaseService
{
    /** 服务端 */
    private $server;

    /** @var array 客户端 */
    private $client = [];

    /** @var string 绑定ip */
    private $ip = '127.0.0.1';

    /** @var string 绑定端口 */
    private $port = '8999';

    protected function init()
    {
        // 初始化 socket
        try {
            if (0 > $this->server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) throw new \Exception('创建 socket 失败：' . socket_strerror($this->server));

            // 绑定 ip:端口
            if (0 > $bind = socket_bind($this->server, $this->ip, $this->port)) throw new \Exception('绑定 ip:端口 失败：' . socket_strerror($bind));

            // 监听
            if (0 > $listen = socket_listen($this->server, 4)) throw new \Exception('监听端口失败：' . socket_strerror($listen));
        } catch (\Exception $exception) {
            Log::channel('socket')->error($exception->getMessage());

            throw new \Exception('初始化 socket 失败！');
        }
    }

    public function boot($output)
    {
        list($write, $error) = [[], []];
        array_push($this->client, $this->server);

        try {

            do {
                $read = $this->client;

                // 查询是否有 连接|接收消息
                if (socket_select($read, $write, $error, null) <= 0) continue;

                // 新客户端连接，握手
                if (in_array($this->server, $read)) {
                    $client = socket_accept($this->server);

                    // 握手
                    $this->encryption($client, socket_read($client, 83886080));

                    // 添加新客户端
                    array_push($this->client, $client);

                    // 去除服务端监听
                    unset($read[array_search($this->server, $read)]);
                }

                // 监听客户端是否有操作
                if ($read) {
                    $msg = '服务器  ======> hello world';

                    foreach ($read as $v) {
                        $content = socket_read($v, 83886080);

                        socket_write($v, $this->frame($msg), strlen($this->frame($msg)));
                    }
                }
            } while (true);
        } catch (\Exception $exception) {
            Log::channel('socket')->error(doEncoding($exception->getMessage()));
        }
    }

    /**
     * 加密（握手）
     *
     * @param $client
     * @param string $content
     */
    private function encryption($client, string $content): void
    {
        $requestHeader = parsingHeaders($content);

        $responseHeader = implode("\r\n", [
                'HTTP/1.1 101 Switching Protocols',
                'Upgrade: websocket',
                'Connection: Upgrade',
                'Sec-WebSocket-Accept: ' . base64_encode(sha1($requestHeader['Sec-WebSocket-Key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true))
            ]) . "\r\n\r\n";

        socket_write($client, $responseHeader, strlen($responseHeader));
    }

    /**
     * 解析数据帧
     *
     * @param $buffer
     * @return string
     */
    private function decode($buffer): string
    {
        $decoded = null;
        $len = ord($buffer[1]) & 127;

        if ($len === 126) {
            $masks = substr($buffer, 4, 4);
            $data = substr($buffer, 8);
        } else if ($len === 127) {
            $masks = substr($buffer, 10, 4);
            $data = substr($buffer, 14);
        } else {
            $masks = substr($buffer, 2, 4);
            $data = substr($buffer, 6);
        }
        for ($index = 0; $index < strlen($data); $index++) {
            $decoded .= $data[$index] ^ $masks[$index % 4];
        }
        return $decoded;
    }

    /**
     * 返回帧信息处理
     *
     * @param $s
     * @return string
     */
    private function frame($s): string
    {
        $a = str_split($s, 125);
        if (count($a) == 1) {
            return "\x81" . chr(strlen($a[0])) . $a[0];
        }
        $ns = "";
        foreach ($a as $o) {
            $ns .= "\x81" . chr(strlen($o)) . $o;
        }
        return $ns;
    }
}